package com.ciei.dpagm.util;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 压缩文件工具类
 * @author fangnuozhou
 * @since 2020/08/28
 */
public class CompressUtils {

    /**
     * 压缩包后缀
     */
    public static final String COMPRESS_FILE_NAME_SUFFIX = ".zip";

    /**
     * 生成压缩文件
     * @param filesToCompress       待压缩的文件路径
     * @param compressedFilePath    压缩后的文件路径
     * @return
     */
    public static File compressFile(List<String> filesToCompress, String compressedFilePath) throws IOException {
        File compress = new File(compressedFilePath);
        if (!compress.exists()) {
            compress.getParentFile().mkdirs();
        }
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(compressedFilePath)))){
            File file;
            for (String fileName : filesToCompress) {
                file = new File(fileName);
                if (file.exists()) {
                    try (FileInputStream inputStream = new FileInputStream(file)) {
                        // 标记要打包的条目
                        zipOutputStream.putNextEntry(new ZipEntry(file.getName()));
                        // 进行写操作
                        int len;
                        byte[] bytes = new byte[1024];
                        while ((len = inputStream.read(bytes)) > 0) {
                            zipOutputStream.write(bytes, 0, len);
                        }
                        zipOutputStream.closeEntry();
                    }
                }
            }
        }
        return compress;
    }

    /**
     * 生成压缩文件
     * @param compressFileMap       待压缩的文件map
     * @param compressedFilePath    压缩后的文件路径
     * @return
     */
    public static File compressFile(Map<String,String> compressFileMap, String compressedFilePath) throws IOException {
        File compress = new File(compressedFilePath);
        if (!compress.exists()) {
            compress.getParentFile().mkdirs();
        }
        try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(compressedFilePath)))){
            File file;
            for (String key : compressFileMap.keySet()) {
                file = new File(key);
                if (file.exists()) {
                    try (FileInputStream inputStream = new FileInputStream(file)) {
                        // 标记要打包的条目
                        zipOutputStream.putNextEntry(new ZipEntry(compressFileMap.get(key)));
                        // 进行写操作
                        int len;
                        byte[] bytes = new byte[1024];
                        while ((len = inputStream.read(bytes)) > 0) {
                            zipOutputStream.write(bytes, 0, len);
                        }
                        zipOutputStream.closeEntry();
                    }
                }
            }
        }
        return compress;
    }

    /**
     * 压缩文件夹
     * @param sourceFolderPath
     * @param zipFilePath
     */
    public static void compressFolderToZip(String sourceFolderPath, String zipFilePath) {
        try (FileOutputStream fos = new FileOutputStream(zipFilePath);
             ZipOutputStream zipOut = new ZipOutputStream(fos)) {
            File fileToZip = new File(sourceFolderPath);
            // 递归压缩目录下的所有文件和子文件夹
            compressFolder(fileToZip, fileToZip.getName(), zipOut);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void compressFolder(File fileToZip, String fileName, ZipOutputStream zipOut) {
        if (fileToZip.isHidden()) {
            return;
        }
        if (fileToZip.isDirectory()) {
            if (fileName.endsWith("/")) {
                try {
                    zipOut.putNextEntry(new ZipEntry(fileName));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            } else {
                try {
                    zipOut.putNextEntry(new ZipEntry(fileName + "/"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                zipOut.closeEntry();
            } catch (IOException e) {
                e.printStackTrace();
            }
            File[] children = fileToZip.listFiles();
            for (File childFile : children) {
                compressFolder(childFile, fileName + "/" + childFile.getName(), zipOut);
            }
            return;
        }

        try (FileInputStream fis = new FileInputStream(fileToZip)) {
            ZipEntry zipEntry = new ZipEntry(fileName);
            zipOut.putNextEntry(zipEntry);

            byte[] bytes = new byte[1024];
            int length;
            while ((length = fis.read(bytes)) >= 0) {
                zipOut.write(bytes, 0, length);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 生成压缩包
     * @param response
     * @param sourceFolderPath
     * @param targetTempFilePath
     * @param fileNamePrefix
     */
    public static void createCompress(HttpServletResponse response, String sourceFolderPath,String targetTempFilePath, String fileNamePrefix) {
        String zipFileName =  fileNamePrefix + COMPRESS_FILE_NAME_SUFFIX;
        //文件压缩输出路径
        String outZipPath = targetTempFilePath + zipFileName;
        //压缩文件到指定目录
        CompressUtils.compressFolderToZip(sourceFolderPath,outZipPath);
        response.setContentType("application/octet-stream");
        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
        response.setHeader("Content-Disposition", "attachment;filename=" + new String(zipFileName.getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1));
        response.addHeader("Cache-Control", "no-cache");
        response.addHeader("File-Md5", FileUtils.getFileMd5(outZipPath));
        try(BufferedInputStream bis = new BufferedInputStream(new FileInputStream(outZipPath));
            BufferedOutputStream bos = new BufferedOutputStream(response.getOutputStream())){
            byte[] buff = new byte[2048];
            int bytesRead;
            while (-1 != (bytesRead = bis.read(buff, 0, buff.length))) {
                bos.write(buff, 0, bytesRead);
            }
        }catch (IOException e) {
            e.printStackTrace();
        }
        FileUtils.deleteFile(targetTempFilePath);
    }


}
